#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>


#include "tr_debug.h"



/** Displays the usage syntax for the program */
void syntax(char *progname)
{
        fprintf(stderr, "Usage: %s  -i input_file [-t task_num] -d\n", progname); 
        fprintf(stderr, "\t -i input_file is the file containing the"
		"dump info.\n\t Or it could be '-' to indicate standard"
		"input \n\n");  
	fprintf(stderr, "\t -t The optional task_num argument filters"
		"the output to only provide \n"); 
	fprintf(stderr, "\t\tthe data relevant to the task numbered as"
		"task_num\n\n");
	fprintf(stderr, "\t -d An optional description field is included.\n"
		"\t\tBy default, it is assumed that this is not included\n\n");

	fprintf(stderr, "\t******** If your output looks garbled try"
		"check the -d option\n\n");

} 

struct platform_info{
	int prio_sort_method;
	int reg_passing;
	int stk_chk_info;


	int tidt_sz; /* Size of tid_t */
	int subtimet_sz; /* Size of subtime */
	int flagt_sz;  /* Size of flagt */
	int oswordt_sz;

	int osptrwordt_sz;  /* Size of pointers */

	int max_procs;

	int gdata_len;
	int data_per_task_len;
	int task_tcb_len;
	int record_sz;

	int description_included;
	int description_len;
};

void print_platform_settings(struct platform_info *p);
void read_until_full(int file_fd, unsigned char * read_ptr, int len) ;

void set_platform_settings( struct os_data_hdr *hdr, 
			    struct platform_info * p) ;


void print_os_record(unsigned char * read_buf, int print_only_task, 
		      struct platform_info *p);

#define MAX_DESC_LEN 512
unsigned char prog_description[MAX_DESC_LEN];

int main(int argc, char ** argv)
{
	struct platform_info p;
	struct os_data_hdr hdr;
	int print_only_task_num = -1;
	int file_fd = -1;
	char ch;
	unsigned char * read_buf;

	memset(prog_description, 0, MAX_DESC_LEN);
	memset(&p, 0, sizeof(struct platform_info));

	// Parse input options.
	if (argc < 2) {
		syntax(argv[0]);
		exit(0);
	}

	while ( (ch = getopt (argc, argv, "hi:t:d")) != EOF)
		switch(ch) {
		case 'h':
			syntax(argv[0]);
			exit(0); break;
		case 'i':
			if ( *optarg == '-') 
				/* Use standard input */
				file_fd = 0;
			else 
				file_fd = open(optarg,	O_RDONLY );
			
			if (file_fd == -1) {
				fprintf(stderr, "open: %s ", optarg);
				perror(" - ");
				goto quit;
			}
			break;
		case 't':
			print_only_task_num = atoi(optarg);
			break;
			
		case 'd':
			p.description_included = 1;
			break;

		default:
			syntax(argv[0]);
			exit(0); break;
		}

	if (p.description_included) {		
		/* Read description length */
		read_until_full(file_fd, &ch, 1);
		p.description_len = ch;
		/* Read description */
		read_until_full(file_fd, prog_description, p.description_len);
	}

	/* Get settings.  The settings are available in all the
	 * packets. Just grab it once from the first packet we see */
	read_until_full(file_fd, (unsigned char *)&hdr, sizeof(hdr));

	fprintf(stderr, "os_options = 0x%02x,  data_sizes1 = 0x%02x, "
	       "data_sizes2 = 0x%02x, max_procs = %d\n", 
	       hdr.os_options, hdr.data_sizes1, hdr.data_sizes2, hdr.os_max_procs);

	set_platform_settings(&hdr, &p);

	fprintf(stderr, "Record size = %d\n", p.record_sz);
	print_platform_settings(&p);

	read_buf = malloc(p.record_sz);
	if (!read_buf) {
		fprintf(stderr, "could not allocate memory\n");
		exit(1);
	}



	/* Read the rest of the data of the first packet */
	read_until_full(file_fd, read_buf + sizeof(hdr),  p.record_sz -
			sizeof(struct os_data_hdr));
	while(1) {
		print_os_record(read_buf, print_only_task_num, &p);
		
		if (p.description_included) {
			/* Skip over the description */
			read_until_full(file_fd, prog_description,
					p.description_len + 1);
		}

		read_until_full(file_fd, read_buf, p.record_sz);
	}



 quit:
	close(file_fd);

	return 0;
}



void read_until_full(int file_fd, unsigned char * read_ptr, int len) 
{
	int rc = 0;
	int len_to_read = len;
	do { 
		rc = read(file_fd, read_ptr, len_to_read);
		if (rc < 0) {
			exit(0);
		}
		if (rc == 0) {
			exit(0);
		}
		len_to_read -= rc;
		read_ptr += rc;
	} while(len_to_read);
}


void set_platform_settings( struct os_data_hdr *hdr, 
			    struct platform_info * p) 
{	
	/*  */
	p->prio_sort_method = TIROS_DEBUG_PRIOSORT_GET(hdr->os_options);
	if ( hdr->os_options & TIROS_DEBUG_REG_PASSING) 
		p->reg_passing = 1;
	
	if (hdr->os_options & TIROS_DEBUG_STKCHK)
		p->stk_chk_info = 1;


	p->tidt_sz = TIROS_DEBUG_GETSZ(hdr->data_sizes1, tid_t_OFFSET );
	p->subtimet_sz = TIROS_DEBUG_GETSZ(hdr->data_sizes1, subtime_t_OFFSET );
	p->flagt_sz = TIROS_DEBUG_GETSZ(hdr->data_sizes1, flag_t_OFFSET );
	p->oswordt_sz = TIROS_DEBUG_GETSZ(hdr->data_sizes1, osword_t_OFFSET);

	p->osptrwordt_sz = TIROS_DEBUG_GETSZ(hdr->data_sizes2, osptrword_t_OFFSET );

	p->max_procs = hdr->os_max_procs;


	p->gdata_len =  sizeof(uint32_t) + p->subtimet_sz +
		4*p->tidt_sz + p->oswordt_sz;
	
	p->task_tcb_len = p->osptrwordt_sz + 
		2*p->tidt_sz +  /* Prio, Eff Prio */
		2*sizeof(uint8_t) +  /* flags, num_mutexes */
		sizeof(uint32_t) + p->subtimet_sz  + /*timeout */
		p->osptrwordt_sz  +      /* lock ptr */
		p->flagt_sz;            /* event bits */
	if (!p->reg_passing)
		p->task_tcb_len += p->osptrwordt_sz;

	if (p->stk_chk_info) 
		p->task_tcb_len += p->osptrwordt_sz + p->oswordt_sz;


	/* os_data_per_task */
	p->data_per_task_len = p->task_tcb_len;   /* TCB */
	p->data_per_task_len += p->osptrwordt_sz;  /* PC */
	if (p->stk_chk_info)                      /* stk usage */
		p->data_per_task_len += 2*p->oswordt_sz;

	if (p->prio_sort_method == 1)            /* list info */
		p->data_per_task_len += 2*p->tidt_sz;

	
	p->record_sz = sizeof(struct os_data_hdr) + p->gdata_len +
		p->data_per_task_len * p->max_procs;


}

void print_platform_settings(struct platform_info *p)
{
	fprintf(stderr, " TIROS Settings\n");
	if (p->description_included)
	    fprintf(stderr, "Description (%d Bytes): %s\n", 
		    p->description_len, prog_description);


	fprintf(stderr, "PrioSort: %d \t reg_passing: %d \t"
	       "stk_chk_info: %d \t oswordt_sz: %d\n",
	       p->prio_sort_method, p->reg_passing,
	       p->stk_chk_info, p->oswordt_sz);
	       
	fprintf(stderr, "tidtsz: %d \t subtimet_sz: %d \t flagt_sz: %d \t"
	       "osptrwordt_sz: %d\n",
	       p->tidt_sz, p->subtimet_sz, p->flagt_sz, p->osptrwordt_sz);

	fprintf(stderr, "max_procs: %d \n",   p->max_procs);
	
	fprintf(stderr, "gdata_len: %d \t data_per_task_len: %d \t"
	       "task_tcb_len: %d \t record_sz: %d\n",
	       p->gdata_len, p->data_per_task_len, p->task_tcb_len,
	       p->record_sz);
}




#define P_hex(b, len)   print_hex(b, len);  b += len;
#define P_dec(b, len)   print_dec(b, len);  b += len;



/**
 * param sz  Size of type (1 byte, 2 , 3, 4, 8 etc.) */
int hex_width(int arg)
{
	int width = 10;
	switch(arg) {
	case 1: width = 4; break;
	case 2: width = 6; break;
	case 4: width = 10; break;
	case 8: width = 18; break;
	}
	return width;
}
/*    string length for 
   8 bit : 2 + 2 = 4
   16 bit: 2 + 4 = 6
   32 bit: 2 + 8 = 10
   64 bit: 2 + 16 = 18 */
void print_hex(unsigned char *b, int sz)
{
	int i;
	unsigned char tmpbuf[8];

	if (sz > 8)
		sz = 8;

	/* The data comes out little endian.
	 * Read it in flipped order. */
	for (i=0; i< sz; i++)
		tmpbuf[sz-1-i] = *b++;

	/* Pipe it out */
	printf("0x");
	for (i=0; i <sz; i++)
		printf("%02x", tmpbuf[i]);


}

/**
 * param sz  Size of type (1 byte, 2 , 3, 4, 8 etc.) */
int dec_width(int arg)
{
	int width = 10;
	switch(arg) {
	case 1: width = 3; break;  
	case 2: width = 5; break;  
	case 4: width = 10; break; 
	case 8: width = 20; break;
	}
	return width;
}
/*    string length for 
   8 bit :  3
   16 bit:  5
   32 bit:  10
   64 bit:  20 */
void print_dec(unsigned char *buf, int sz)
{
	unsigned char b[8] = {0, 0, 0, 0,   0, 0, 0, 0};
	uint8_t tmp8;
	uint16_t tmp16;
	uint32_t tmp32;
	uint64_t tmp64;
	int i;

	if (sz > 8)
		sz = 8;

	for(i=0; i<sz; i++) {
		b[sz-1-i] = *buf++;
	}


	switch(sz) {
	case 1:
		tmp8 = b[0];
		printf("%3u", tmp8);
		break;
	case 2:
		tmp16 = ((uint16_t)b[0] << 8) | (uint16_t)b[1];
		printf("%5hu", tmp16);
		break;
	case 4:
		tmp32 = ((uint32_t)b[0] << 24) | 
			((uint32_t)b[1] << 16) |
			((uint32_t)b[2] << 8 ) |
			(uint32_t)b[3];
		printf("%10lu", (uint32_t)tmp32);
		break;

	case 8:
		tmp64 = ((uint64_t)b[0] << 56) |
			((uint64_t)b[1] << 48) |
			((uint64_t)b[2] << 40) |
			((uint64_t)b[3] << 32) |
			((uint64_t)b[4] << 24) |
			((uint64_t)b[5] << 16) |
			((uint64_t)b[6] << 8 ) |
			(uint64_t)b[7];
		printf("%20llu", tmp64);
		break;
	}
}


void align_print(char * str, int fieldsz)
{
	int strwidth;
	int tot_space_in_tabs;
	int str_space;


	printf("%s", str);
	strwidth = strlen(str);
	tot_space_in_tabs = (fieldsz + 8)/8;

	str_space = strwidth/8;
	/* How many tabs do we need */
	for(; str_space < tot_space_in_tabs; str_space++)
		printf("\t");
}

void print_os_legend(struct platform_info *p)
{
	align_print("TSK",  dec_width(p->tidt_sz));
	align_print("CP",   hex_width(p->osptrwordt_sz));
	align_print("PRIO", dec_width(p->tidt_sz));
	align_print("EFF",   dec_width(p->tidt_sz));


	align_print("FLAGS", hex_width(sizeof(uint8_t)));
	align_print("MUTXS", hex_width(sizeof(uint8_t)));
	align_print("TIMEOUT", dec_width(sizeof(uint32_t)) +
		1 + dec_width(p->subtimet_sz)); 


	align_print("LK_PTR", hex_width(p->osptrwordt_sz));
	align_print("EVTS", hex_width(p->flagt_sz));

	if (! p->reg_passing) {
		align_print("RETVAL", hex_width(p->osptrwordt_sz));
	}
		
	if (p->stk_chk_info) {
		align_print("STK", hex_width(p->osptrwordt_sz));
		align_print("STKSZ", dec_width(p->oswordt_sz));
	}
				
	align_print("PC", hex_width(p->osptrwordt_sz));
	if (p->stk_chk_info) {
		align_print("CUR_STK", dec_width(p->oswordt_sz));
		align_print("MAX_STK", dec_width(p->oswordt_sz));
	}

		
	if (p->prio_sort_method == 1) {
		align_print("R/W Q", dec_width(p->tidt_sz));
		align_print("LK Q", dec_width(p->tidt_sz));
	}
	printf("\n");
}

void print_os_record(unsigned char * read_buf, int print_only_tasknum, 
		     struct platform_info *p)
{
	int tasknum;
	unsigned char *buf_loc = read_buf;
	int i;


	buf_loc += sizeof(struct os_data_hdr);


	for (i=0; i<80; i++)
		printf("*");
	printf("\n");

	/* Print out time */
	P_dec(buf_loc, sizeof(uint32_t)); 
	printf(":");
	P_dec(buf_loc, p->subtimet_sz);   
	
	printf("\tRUN:");
	P_dec(buf_loc, p->tidt_sz);
	printf("\tRDY:");
	P_dec(buf_loc, p->tidt_sz);   
	printf("\tWT:");
	P_dec(buf_loc, p->tidt_sz);   
	printf("\tNESTING:");
	P_dec(buf_loc, p->tidt_sz);   
	
	/* context switch count */
	printf("\tCTXT_CNT:");
	P_dec(buf_loc, p->oswordt_sz);

	printf("\n");

	for (i=0; i<80; i++)
		printf("-");
	printf("\n");
	print_os_legend(p);

	for ( tasknum = 0; tasknum < p->max_procs ; tasknum++ ) {
		if ( print_only_tasknum != -1 &&
		     tasknum != print_only_tasknum) {
			buf_loc += p->data_per_task_len;
			continue;
		}		

		printf("%d", tasknum); printf("\t");

		P_hex(buf_loc, p->osptrwordt_sz); 
		printf("\t");
		P_dec(buf_loc, p->tidt_sz);       /* Prio */
		printf("\t");
		P_dec(buf_loc, p->tidt_sz);       /* Eff Prio */
		printf("\t");
		P_hex(buf_loc, sizeof(uint8_t));  /* flags */
		printf("\t");
		P_dec(buf_loc, sizeof(uint8_t));  /* mutexes */
		printf("\t");
		P_dec(buf_loc, sizeof(uint32_t)); /* timeout */
		printf(":");
		P_dec(buf_loc, p->subtimet_sz);   
		printf("\t");
		
		P_hex(buf_loc, p->osptrwordt_sz); /* lock ptr */
		printf("\t");
		P_hex(buf_loc, p->flagt_sz);  /* event bits */
		printf("\t");
		
		if (! p->reg_passing) {
			P_hex( buf_loc, p->osptrwordt_sz);
			printf("\t");
		}


		
		if (p->stk_chk_info) {
			P_hex(buf_loc, p->osptrwordt_sz);
			printf("\t");
			P_dec(buf_loc, p->oswordt_sz);
			printf("\t");
		}
				
		P_hex(buf_loc, p->osptrwordt_sz);  /* PC */
		printf("\t");

		if (p->stk_chk_info) {
			P_dec(buf_loc, p->oswordt_sz);    /* curr stack
							     occupancy
							     */
			printf("\t");
			P_dec(buf_loc, p->oswordt_sz);    /* maximum stack
							     occupancy */
		
			printf("\t");
		}

		
		if (p->prio_sort_method == 1) {
			P_hex(buf_loc, p->tidt_sz);
			printf("\t");
			P_hex(buf_loc, p->tidt_sz);
			printf("\t");
		}

		printf("\n");

	}	

	
}



